package view_server

import (
	"bigdevops/src/cache"
	"bigdevops/src/common"
	"bigdevops/src/config"
	"bigdevops/src/models"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"go.uber.org/zap"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/util/json"
	"sigs.k8s.io/yaml"
	"sort"
	"strconv"
	"strings"
)

type OneService struct {
	Cluster    string `json:"cluster"`
	Name       string `json:"name"`
	Namespace  string `json:"namespace"`
	Age        string `json:"age"`
	Type       string `json:"type"`
	ClusterIp  string `json:"clusterIp"`
	ExternalIp string `json:"externalIp"`
	// 443/TCP              服务端口/协议
	// 3306:30016/TCP       服务端口:nodePort/协议
	SvcPorts string `json:"svcPorts"`
	// ports 是为了给前端编辑的时候 展示端口的
	Ports       []corev1.ServicePort `json:"ports"`
	Selectors   []*common.OneLabels  `json:"selectors"`
	PodSelector string               `json:"podSelector"`

	LabelPairs  map[string]string `json:"labelPairs"`
	Labels      []string          `json:"labels"`
	LabelsFront string            `json:"labelsFront"`
}

func getK8sServiceListByNs(c *gin.Context) {

	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	currentPage, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
	sortFiled := c.DefaultQuery("field", "")
	orderType := c.DefaultQuery("order", "")

	// 集群的名称
	searchCluster := c.DefaultQuery("cluster", "")
	searchName := c.DefaultQuery("name", "")

	searchNamespace := c.DefaultQuery("namespace", "")
	if searchNamespace == common.K8S_NS_ALL_KEY {
		searchNamespace = ""
	}

	if searchCluster == "" {
		msg := "集群名称未传入"
		sc.Logger.Error(msg)
		common.ReqBadFailWithMessage(msg, c)
		return
	}

	dbObj, err := models.GetK8sClusterByName(searchCluster)
	if err != nil {
		sc.Logger.Error("根据name找k8s集群错误", zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	mSet := kc.GetClusterMetricsClientSetById(dbObj.ID)
	if mSet == nil {
		sc.Logger.Error("根据id找k8s集群metricsSet缓存错误", zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	ctx1, cancel1 := common.GenTimeoutContext(dbObj.ActionTimeoutSeconds)
	defer cancel1()
	svcItems, err := kSet.CoreV1().Services(searchNamespace).List(ctx1, metav1.ListOptions{})
	if err != nil {
		sc.Logger.Error("根据ik8s集群kset获取节点错误", zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 进行搜索
	filterResults := []corev1.Service{}
	for _, svc := range svcItems.Items {
		if searchName != "" && !strings.Contains(svc.Name, searchName) {
			continue
		}
		filterResults = append(filterResults, svc)

	}

	//startIndex, endIndex
	startIndex := (pageSize) * (currentPage - 1)
	endIndex := startIndex + pageSize
	if endIndex > len(filterResults) {
		endIndex = len(filterResults)
	}

	// 转化一下
	finalRes := []*OneService{}
	pageResult := filterResults[startIndex:endIndex]

	for _, obj := range pageResult {
		one := serviceConvert(obj)
		finalRes = append(finalRes, one)
	}
	// 排序
	if sortFiled == "name" {

		sort.SliceStable(finalRes, func(i, j int) bool {
			if orderType == "ascend" {
				return finalRes[i].Name < finalRes[j].Name
			} else {
				return finalRes[i].Name > finalRes[j].Name
			}

		})
	}

	resp := &ResponseResourceCommon{
		//Total: models.GetK8sClusterCount(), 因为是带了查询条件的 所以 count 不能是all 而是 过滤后的total

		Total: len(filterResults),
		Items: finalRes,
	}

	common.OkWithDetailed(resp, "ok", c)
}

// 学习k8s源码中的 kubectl get svc 的方法
// printer 位置 D:\nyy_work\go_path\src\github.com\kubernetes\kubernetes\pkg\printers\internalversion\printers.go
/*

	serviceColumnDefinitions := []metav1.TableColumnDefinition{
		{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
		{Name: "Type", Type: "string", Description: apiv1.ServiceSpec{}.SwaggerDoc()["type"]},
		{Name: "Cluster-IP", Type: "string", Description: apiv1.ServiceSpec{}.SwaggerDoc()["clusterIP"]},
		{Name: "External-IP", Type: "string", Description: apiv1.ServiceSpec{}.SwaggerDoc()["externalIPs"]},
		{Name: "Port(s)", Type: "string", Description: apiv1.ServiceSpec{}.SwaggerDoc()["ports"]},
		{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
		{Name: "Selector", Type: "string", Priority: 1, Description: apiv1.ServiceSpec{}.SwaggerDoc()["selector"]},
	}

	_ = h.TableHandler(serviceColumnDefinitions, printService)
	_ = h.TableHandler(serviceColumnDefinitions, printServiceList)

*/

func makePortString(ports []corev1.ServicePort) string {
	pieces := make([]string, len(ports))
	for ix := range ports {
		port := &ports[ix]
		pieces[ix] = fmt.Sprintf("%d/%s", port.Port, port.Protocol)
		if port.NodePort > 0 {
			pieces[ix] = fmt.Sprintf("%d:%d/%s", port.Port, port.NodePort, port.Protocol)
		}
	}
	return strings.Join(pieces, ",")
}

// 从k8s中获取到的svc 转化成前端需要的字段
func serviceConvert(svc corev1.Service) *OneService {
	res := &OneService{
		Name:      svc.Name,
		Namespace: svc.Namespace,
		Age:       translateTimestampSince(svc.CreationTimestamp),
	}

	res.Type = string(svc.Spec.Type)
	res.ClusterIp = svc.Spec.ClusterIP

	eip := "<none>"
	if len(svc.Spec.ExternalIPs) > 0 {
		eip = strings.Join(svc.Spec.ExternalIPs, ",")
	}
	res.ExternalIp = eip
	res.SvcPorts = makePortString(svc.Spec.Ports)
	res.Ports = svc.Spec.Ports

	ss := []*common.OneLabels{}
	for k, v := range svc.Spec.Selector {
		ss = append(ss, &common.OneLabels{
			Key:   k,
			Value: v,
		})
	}
	res.Selectors = ss
	res.PodSelector = labels.FormatLabels(svc.Spec.Selector)

	return res
}

// 反过来的操作
func serviceConvertBack(one *OneService) *corev1.Service {
	svc := &corev1.Service{}
	svc.Name = one.Name
	svc.Namespace = one.Namespace
	svc.Spec.Ports = one.Ports
	sm := map[string]string{}
	for _, sor := range one.Selectors {
		sor := sor
		if sor.Key == "" {
			continue
		}

		if sor.Value == "" {
			continue
		}
		sm[sor.Key] = sor.Value
	}

	svc.Spec.Selector = sm
	svc.Spec.Type = corev1.ServiceType(one.Type)
	return svc
}

func getK8sServiceYamlOne(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	searchCluster := c.DefaultQuery("cluster", "")
	searchName := c.DefaultQuery("name", "")

	searchNamespace := c.DefaultQuery("namespace", "")

	if searchCluster == "" {
		msg := "集群名称未传入"
		sc.Logger.Error(msg)
		common.ReqBadFailWithMessage(msg, c)
		return
	}

	dbObj, err := models.GetK8sClusterByName(searchCluster)
	if err != nil {
		sc.Logger.Error("根据name找k8s集群错误", zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	ctx1, cancel1 := common.GenTimeoutContext(dbObj.ActionTimeoutSeconds)
	defer cancel1()
	svc, err := kSet.CoreV1().Services(searchNamespace).Get(ctx1, searchName, metav1.GetOptions{})
	//fmt.Println(pod)
	svc.APIVersion = "v1"
	svc.Kind = "Service"
	svc.ManagedFields = nil

	if err != nil {
		msg := fmt.Sprintf("根据id找k8s集群get-svc错误:%v", err.Error())
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	jsonBytes, err := json.Marshal(svc)
	if err != nil {
		msg := fmt.Sprintf("根据svc对象jsonMarshal错误:%v", err.Error())
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}
	y, err := yaml.JSONToYAML(jsonBytes)
	if err != nil {
		msg := fmt.Sprintf("根据svc对象jsonyaml.JSONToYAML错误:%v", err.Error())
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	common.OkWithData(string(y), c)
}

// 更新只能更新 端口和pod选择器 ，name ns type禁止更新
func k8sCreateOrUpdateServiceOne(c *gin.Context) {
	//kset *kubernetes.Clientset, podName string, containerName string, ns string string
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	var reqObj OneService
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析k8sUpdateConfigmapDataOne配置请求失败", zap.Any("k8s配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors1, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors1.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	searchCluster := reqObj.Cluster
	searchName := reqObj.Name
	searchNamespace := reqObj.Namespace

	if searchCluster == "" {
		msg := "集群名称未传入"
		sc.Logger.Error(msg)
		common.ReqBadFailWithMessage(msg, c)
		return
	}

	dbObj, err := models.GetK8sClusterByName(searchCluster)
	if err != nil {
		sc.Logger.Error("根据name找k8s集群错误", zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	//  转化备用的svc
	newSvc := serviceConvertBack(&reqObj)

	ctx1, canFun1 := common.GenTimeoutContext(dbObj.ActionTimeoutSeconds)
	defer canFun1()
	svc, err := kSet.CoreV1().Services(searchNamespace).Get(ctx1, searchName, metav1.GetOptions{})
	if err != nil {
		// 非not found的错误
		if !errors.IsNotFound(err) {
			msg := fmt.Sprintf("创建或更新svc的data前置获取svc对象错误:%v", err.Error())
			sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
			common.FailWithMessage(msg, c)
			return

		}
		// 没有这个对象 ，创建即可
		ctx2, canFun2 := common.GenTimeoutContext(dbObj.ActionTimeoutSeconds)
		defer canFun2()
		newSvc, err = kSet.CoreV1().Services(searchNamespace).Create(ctx2, newSvc, metav1.CreateOptions{})
		if err != nil {
			msg := fmt.Sprintf("创建svc错误:%v", err.Error())
			sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
			common.FailWithMessage(msg, c)
			return
		}
		common.OkWithMessage("svc创建成功", c)
		return
	}

	// 走到这里说明一定查询到了一个 svc，要更新
	svc.Spec.Ports = reqObj.Ports
	svc.Spec.Selector = newSvc.Spec.Selector
	ctx3, canFun3 := common.GenTimeoutContext(dbObj.ActionTimeoutSeconds)
	defer canFun3()
	_, err = kSet.CoreV1().Services(searchNamespace).Update(ctx3, svc, metav1.UpdateOptions{})
	if err != nil {
		msg := fmt.Sprintf("更新svc错误:%v", err.Error())
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	common.OkWithMessage("更新成功", c)

}

func k8sBatchDeleteService(c *gin.Context) {
	//kset *kubernetes.Clientset, podName string, containerName string, ns string string
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	var reqObj common.K8sBatchRequestCommon
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析批量删除Deployment请求失败", zap.Any("k8s配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	//searchCluster := c.DefaultQuery("cluster", "")
	searchCluster := reqObj.Cluster

	if searchCluster == "" {
		msg := "集群名称未传入"
		sc.Logger.Error(msg)
		common.ReqBadFailWithMessage(msg, c)
		return
	}

	dbObj, err := models.GetK8sClusterByName(searchCluster)
	if err != nil {
		sc.Logger.Error("根据name找k8s集群错误", zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	for _, item := range reqObj.Items {
		item := item
		ctx1, canFun1 := common.GenTimeoutContext(dbObj.ActionTimeoutSeconds)
		defer canFun1()
		err := kSet.CoreV1().Services(item.Namespace).Delete(ctx1, item.Name, metav1.DeleteOptions{})
		if err != nil {
			msg := fmt.Sprintf("删除svc错误:ns:%v name:%v %v", item.Namespace, item.Name, err.Error())
			sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
			common.FailWithMessage(msg, c)
			return
		}

	}

	common.OkWithMessage("删除成功", c)

}
